package com.sakiko.service.impl;

import com.sakiko.dao.StudentDao;
import com.sakiko.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentDao studentDao;

//    @Override
//    public int modifyAgeById(Integer id, Integer age) {
//        return studentDao.updateAgeById(id, age);
//    }
//
//    @Override
//    public int modifyGenderById(Integer id, String gender) {
//        return studentDao.updateGenderById(id, gender);
//    }

    /**
     * @Transactional == 声明式事务  ==> [1、事务管理器对象加入到 ioc容器 2、开启事务支持]
     * <p>
     * detail 1: 位置问题 @Target({ElementType.TYPE, ElementType.METHOD})
     * a.方法上: 指定方法进行事务添加
     * b.类上: 指定整个类的所有方法都添加事务 【开发】
     * 同时添加，方法属性优先
     * detail 2: 只读模式 boolean readOnly() default false;
     * 默认非只读，代表当前事务可查询，也可以修改数据
     * readOnly = true 只读，代表当前事务只能查询，不可以修改
     * 只读的优势: mysql有两个通道，一个查询，一个修改！这里的 "只读"关闭修改通道！这样可以提高查询效率
     * 矛盾点: readOnly = true -> 方法 -> 只有查询语句 -> 查询语句又不需要修改
     * 解释: 1、不添加事务，默认值是非只读！效率角度上。可以显示的添加 readOnly = true 开启只读模式。提高查询效率！！！
     * 2、很多时候，都是在业务类上添加事务，所有的方法都有事务，且默认一定是非只读！可以在只有查询语句的方法上添加事务，设置只读模式
     * note: readOnly了解即可，有这个效率原因，但是真追求效率就别用 mysql,那样要比这个好维护、效率高
     * detail 3: 超时时间 timeout() default -1;
     * 1、timeout = -1 代表事务永远不超时，前端客户端必须等待我们返回结果！！
     * 2、timeout = int 秒，如果超过了对应的时间，事务还没有执行完毕，会主动抛出异常！
     * detail 4: 处理异常类型
     * 1、 Throwable>[] rollbackFor() default {}; 默认是 RunTimeException/Error及其子类，不包括 eg: FileNotFoundException
     * 2、 Throwable>[] noRollbackFor() default {}; 默认 空  要求: 必须是 rollBackFor范围内的异常！！！
     * detail 5: 事务隔离级别设置 Isolation isolation() default Isolation.DEFAULT;
     * 1、设置事务隔离级别
     * DEFAULT(-1),
     * READ_UNCOMMITTED(1),
     * READ_COMMITTED(2),
     * REPEATABLE_READ(4),
     * SERIALIZABLE(8);
     * detail 6: 事务传播行为   Propagation propagation() default Propagation.REQUIRED;
     * 1、事务传播行为，只有在多事务互相调用的场景才会生效！！
     *  父事务 -> 调用 -> 子事务 (事物的传播行为，决定子事务是否添加到父事务中，形成一个事务！！！)
     *  事务传播行为的默认值 Propagation.REQUIRED: 方法在一个已经存在事务的方法中被调用，它们将共享同一个事务；否则，它们会启动自己的事务。
     *  事物的传播行为必须是不同的 service对象之间调用才可以生效
     *  【属性值解释】
     *      REQUIRED(0)     - 若存在事务则加入，否则创建新事务
     *      SUPPORTS(1)     - 若存在事务则加入，否则非事务执行
     *      MANDATORY(2)    - 必须在已存在的事务中执行，否则抛异常
     *      REQUIRES_NEW(3) - 始终创建新事务，挂起当前事务（若存在）
     *      NOT_SUPPORTED(4)- 非事务执行，挂起当前事务（若存在）
     *      NEVER(5)        - 非事务执行，若存在事务则抛异常
     *      NESTED(6)       - 若存在事务则嵌套执行，否则创建新事务
     *  补充: "挂起当前事务" 指的是暂停外层（调用方）事务的执行，直到被调用方法的新事务完成。
     */
//    @Transactional // 第三步: 声明
    @Transactional(readOnly = false, //  readOnly = true => Queries leading to data modification are not allowed
            timeout = 3, // TransactionTimedOutException: Transaction timed out: deadline was ...
            rollbackFor = Exception.class,
            noRollbackFor = FileNotFoundException.class,
            isolation = Isolation.READ_COMMITTED)

    @Override
    public void modifyInfo() /*throws FileNotFoundException*/ {
        System.out.println("modify.....");
        // 操作一
        studentDao.updateAgeById(6, 20);
//        int i = 1 / 0;
//        try {
//            Thread.sleep(3010);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
        // rollbackFor() 默认值中不包括 FileNotFoundException 下面异常出现也不回滚
//            FileInputStream aaaaaa = new FileInputStream("aaaaaabbbbbbbbbbbbccccccc");

        System.out.println("modify.....");
        // 操作二
        studentDao.updateGenderById(6, "男");
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public int modifyAgeById() {
        return studentDao.updateAgeById(2, 999);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public int modifyGenderById() {
        int a = studentDao.updateGenderById(6, "事物的传播行为");
        int i = 1 / 0; // 子事务 2出错 1也回滚，因为 TopService调用，形成同一个事务
        return a;
    }
}
/*
数据库事务的隔离级别是指在多个事务并发执行时，数据库系统为了保证数据一致性所遵循的规定。
常见的隔离级别包括：
1. 读未提交（Read Uncommitted）：事务可以读取未被提交的数据，容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全，一般不用。
2. 读已提交（Read Committed）：事务只能读取已经提交的数据，可以避免脏读问题，但可能引发不可重复读和幻读。 【oracle】
3. 可重复读（Repeatable Read）：在一个事务中，相同的查询将返回相同的结果集，不管其他事务对数据做了什么修改。可以避免脏读和不可重复读，但仍有幻读的问题。 【mysql】
4. 串行化（Serializable）：最高的隔离级别，完全禁止了并发，只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题，但效率较低，不适用于高并发场景。
   不同的隔离级别适用于不同的场景，需要根据实际业务需求进行选择和调整。
 */